
/*
        路由匹配模块
        一个消息的发布，先交给交换机。
        再根据交换机的路由模式：广播路由，直接路由，主题路由

    一条消息包含rounting_key，交换机与队列的绑定关系包含binding_key
    1，广播路由：交换机收到一条消息，就直接这条消息路由给绑定的所有的队列
    2，直接路由：当一个队列与交换机的binding_key与消息的routing_key完全相同时，才将这条消息路由给这个队列。
    3，主题路由：当一个队列与浇花呢的binding_key与消息的routing_key匹配成功时，才将这条消息路由给这个队列。
        使用到一种匹配算法。
*/
#ifndef __M_ROUTE_H__
#define __M_ROUTE_H__
#include "../mqcommon/helpper.hpp"
#include "../mqcommon/logger.hpp"
#include "../mqcommon/mq_message.pb.h" //消息的结构化描述在该部分使用protobuf生成
#include <iostream>
#include <string>

namespace xgmq
{
    class Route
    {
    public:
        // 判断routing_key是否合法
        static bool isLegalRouting_key(const std::string &routing_key)
        {
            // 对于routing_key，其中应该只包含字符0~9,a~z,A~Z,.,_等
            for (int i = 0; i < routing_key.size(); i++)
            {
                if ((routing_key[i] >= '0' && routing_key[i] <= '9') ||
                    (routing_key[i] >= 'a' && routing_key[i] <= 'z') ||
                    (routing_key[i] >= 'A' && routing_key[i] <= 'Z') ||
                    (routing_key[i] == '_' || routing_key[i] == '.'))
                {
                    continue;
                }
                return false;
            }
            return true;
        }

        // 判断binding_key是否合法
        static bool isLegalBinding_key(const std::string &binding_key)
        {
            // 1,对于binding_key，除了字符0~9,a~z,A~Z,.,_等，还包含通配符*,#  
            for (int i = 0; i < binding_key.size(); i++)
            {
                if ((binding_key[i] >= '0' && binding_key[i] <= '9') ||
                    (binding_key[i] >= 'a' && binding_key[i] <= 'z') ||
                    (binding_key[i] >= 'A' && binding_key[i] <= 'Z') ||
                    (binding_key[i] == '_' || binding_key[i] == '.') ||
                    (binding_key[i] == '*' || binding_key[i] == '#'))
                {
                    continue;
                }
                return false;
            }
            //2,对于通配符，*匹配任意一个单词,#匹配任意零个或多个单词
            //通配符必须是独立存在的，也就是不能和其他单词一起出现.
            //比如aa#.b是不合法的,aab.#才是合法的，aa*.b是不合法的，aab.*是合法的
            //因为这里我们规定的是通配符匹配的是单词，而不是字符
            std::vector<std::string> result;
            StrSplit::split(binding_key,".",result);//将binding_key按照字符'.'进行分割，将分割出的单词放入result中
            //判断每个单词是否合法
            for(auto& word:result)
            {
                if(word.size()>1&&word.find('*')!=std::string::npos)
                {
                    return false;
                }
                if(word.size()>1&&word.find('#')!=std::string::npos)
                {
                    return false;
                }
            }
            //3，对于binding_key，通配符#的左右两边是不能有其他通配符的，这是没有意义的
            for(int i=1;i<result.size();i++)
            {
                if(result[i]=="#"&&result[i-1]=="#")
                {
                    return false;
                }
                if(result[i]=="#"&&result[i-1]=="*")
                {
                    return false;
                }
                if(result[i]=="*"&&result[i-1]=="#")
                {
                    return false;
                }
            }
            return true;
        }

        //判断一条消息是否可以传给某个交换机
        //需要交换机的类型，消息的routing_key，交换机和队列绑定关系的binding_key
        static bool route(ExchangeType type, const std::string &binding_key, const std::string &routing_key)
        {
            if(type==ExchangeType::DIRECT)//直接交换
            {
                return (binding_key==routing_key);
            }
            else if(type==ExchangeType::FANOUT)//广播交换
            {
                return true;
            }
            //主题交换，只有routing_key与bidning_key匹配了，才可以交换
            //1,先将binding_key与routing_key按照'.'进行分割,存放在数组中
            std::vector<std::string> b_key,r_key;
            int n_bind=StrSplit::split(binding_key,".",b_key);
            int n_route=StrSplit::split(routing_key,".",r_key);

            //2,初始化二维dp表，dp[0][0]置为true,如果binding_key的第一个位置为'#'，那么第一行的第0列初始化为true
            //洽谈位置初始化为false
            std::vector<std::vector<bool>> dp(n_bind+1,std::vector<bool>(n_route+1,false));
            dp[0][0]=true;
            if(b_key[0]=="#")
            {
                dp[1][0]=true;
            }

            //3,完成dp表
            for(int i=1;i<=n_bind;i++)
            {
                for(int j=1;j<=n_route;j++)
                {
                    if(b_key[i-1]=="#")
                    {
                        dp[i][j]=dp[i-1][j-1]|dp[i][j-1]|dp[i-1][j];
                    }
                    else if(b_key[i-1]=="*"||(b_key[i-1]==r_key[j-1]))
                    {
                        dp[i][j]=dp[i-1][j-1];
                    }
                }
            }
            return dp[n_bind][n_route];
        }
    };
}
#endif